local class = require 'lib/middleclass'
local stalker = require 'lib/STALKER-X'
local animate = require 'lib/love-animation'
local i18n = require 'lib/i18n'
local sti = require 'lib/Simple-Tiled-Implementation/sti'
local lynx = require 'lib/lynx'
local lynx_funcs = lynx.love_lynx

LEFT = -4; RIGHT = -3; UP = -2; DOWN = -1
currentRoom = nil -- Just in case. Gets overriden, but juuust in case.
states = {}

function love.load()
	love.graphics.setDefaultFilter("nearest", "nearest")
	game init()
	camera = stalker() -- Dependaĵoj: retfilmilo
end

-- Main loop
-- t – Time since last update.
function love.update(t)
	state function("update", t)
end

function love.draw()
	--camera:attach()
	
	state function("draw")
	
	--camera:detach()
	--camera:draw()
end

function love.keypressed(key)
	state function("keypressed", key)
end

function love.keyreleased(key)
	state function("keyreleased", key)
end

-- STATES :: SUFOKIĜU :: ℋ𝒜𝒮𝒦ℰℒℒ 𝒞ℛℰ𝒲
--------------------------------------------------------------------------------
-- GAME
----------------------------------------
--[[
function·call U+00B7 MIDDLE DOT
function’call
function🦆call,<<<
function🕴call
blessed†function†invocation
function⁂call
function‣call
function⁝call
newUpdate, newDraw, newPress, newRelease,
]]--

function game init()
	currentRoom = Room:new('maps/Domo.lua') -- nil ← was before	
	local functions = {
		["update"] = { ["func"] = game update, ["combine"] = false },
		["draw"] = { ["func"] = game draw, ["combine"] = false },
		["keypressed"] = { ["func"] = game keypressed, ["combine"] = false },
		["keyreleased"] = { ["func"] = game keyreleased, ["combine"] = false }
	}

	local game mode = { ["functions"] = functions, ["name"] = "game" }
	add state(game mode)
	--test menu:open()
end

function game update(t)
	currentRoom:update(t)
end

function game draw()
	currentRoom:draw()
end

function game keypressed(key)
	currentRoom:keypressed(key)
end

function game keyreleased(key)
	currentRoom:keyreleased(key)
end



-- ^CLASSES <<<< ~~~ ** ""< <<<<<<<< TIE <<< ĜI SUPERAS, ĈU VI RIMARKIS AŬ NE??? BONVOLE RESPONDU
--------------------------------------------------------------------------------
-- CHARACTER
----------------------------------------
Character = class("Character")
function Character:initialize(x, y, sprite file)
	self.animation = LoveAnimation.new(sprite file)
	self.loc = { ["x"]= x, ["y"] = y}
    self.z = nil --[[Oseble]]--
	self.direction = RIGHT
		-- Right or left― which way the character is facing.
	self.directionals = { RIGHT = 0, LEFT = 0, UP = 0, DOWN = 0 }
		-- Directionals show the ascending order of currently pressed buttons.
	self.walking = false
    self.walkspeed = 1
    self.move order = nil
end

function Character:draw()
	self.animation:draw(2, 2)
end

function Character:update(t)
	self:movement(t)
	self:move to update()
	self:directionals update()
	self:animation update(t)

	
end

function Character:directionals update()
	local dirs = self.directionals
	if (dirs[LEFT] == 2 and dirs[RIGHT] == 0) then dirs[LEFT] = 1; end
	if (dirs[RIGHT] == 2 and dirs[LEFT] == 0) then dirs[RIGHT] = 1; end
	if (dirs[UP] == 2 and dirs[DOWN] == 0) then dirs[UP] = 1; end
	if (dirs[DOWN] == 2 and dirs[UP] == 0) then dirs[DOWN] = 1; end
	
	self.walking = false
	for direction,queue pos in pairs(dirs) do
		if (queue pos == 1) then
			self.walking = true
		end
	end
end

function Character:animation update(t)
	self.animation:setPosition(self.loc.x, self.loc.y)
	self.animation:update(t)
	
	if (self.walking == true) then
		if (self.direction == LEFT) and (self.animation:getCurrentState() ~= "walkleft") then
			self.animation:setState("walkleft")
		elseif (self.direction == RIGHT) and (self.animation:getCurrentState() ~= "walkright") then
			self.animation:setState("walkright")
		end
	else
		if (self.direction == LEFT) and (self.animation:getCurrentState() ~= "inactiveleft") then
			self.animation:setState("inactiveleft")
		elseif (self.direction == RIGHT) and (self.animation:getCurrentState() ~= "inactiveright") then
			self.animation:setState("inactiveright")
		end
	end
end

function Character:movement(t)
	local walkspeed = self.walkspeed
	if (self.direction == LEFT) or (self.direction == UP) then
		walkspeed = -walkspeed
	end

	if (self.walking == true) then		
		-- actually moving    - ←←←←←←←←←←←←←←
		if (self.direction == LEFT) or (self.direction == RIGHT) then
			self.loc.x = self.loc.x + walkspeed
		end
		if (self.direction == UP) or (self.direction == DOWN) then
			self.loc.y = self.loc.y + walkspeed
		end
	end
	
	if (self.walking == false) then
		if (self.direction == LEFT) and (self.animation:getCurrentState() ~= "inactiveleft") then
			self.animation:setState("inactiveleft")
		elseif (self.direction == RIGHT) and (self.animation:getCurrentState() ~= "inactiveright") then
			self.animation:setState("inactiveright")
		end
	end
end

function Character:move to(x, y)
    self.move order = { ["x"] = x, ["y"] = y}
end

function Character:move to update(t)
    if (self.move order == nil) then
        return
    end

    self.walking = true
    if (self.loc.x < self.move order.x) then
        self.direction = RIGHT
    elseif (self.loc.x > self.move order.x) then
        self.direction = LEFT
    elseif (self.loc.y < self.move order.y) then
        self.direction = DOWN
     elseif (self.loc.y > self.move order.y) then
        self.direction = UP
    else
        self.move order = nil
        self.walking = false
    end
end

function Character:keypressed(key)
end

function Character:keyreleased(key)
end


-- PLAYER
----------------------------------------
Player = class("Player", Character)

function Player:initialize(x, y, sprite file)
	Character.initialize(self, x, y, sprite file)
	self.keymap = { ["left"] = LEFT, ["right"] = RIGHT, ["up"] = UP, ["down"] = DOWN }
	self.actionmap = invert table(self.keymap)
end

function Player:keypressed(key)
	local dirs = self.directionals
	local dir = self.keymap[key]
	if (dir == RIGHT) or (dir == LEFT) then
		self.direction = dir
	end
	
	if (dir == RIGHT) then -- you’re going in the right direction
		dirs[RIGHT] = 1;	if (dirs[LEFT] == 1) then dirs[LEFT] = 2; end
	elseif (dir == LEFT) then
		dirs[LEFT] = 1;	if (dirs[RIGHT] == 1) then dirs[RIGHT] = 2; end
	elseif (dir == UP) then
		dirs[UP] = 1;	if (dirs[DOWN] == 1) then dirs[DOWN] = 2; end
	elseif (dir == DOWN) then -- you’re going DOWN >:-)
		dirs[DOWN] = 1;	if (dirs[UP] == 1) then dirs[UP] = 2; end
	end
end

function Player:keyreleased(key)
	local dirs = self.directionals
	local dir = self.keymap[key]
	if (dirs[dir] ~= nil) then
		dirs[dir] = 0
	end
end

-- ROOM
----------------------------------------
Room = class("Room")

function Room:initialize(map file)
	local ludato = Player:new(10, 10, "art/dwarf.lua")
    ludato:move to(100, 100)
	self.characters = { ludato }
    self.things = { }

    self.map = sti(map file)
end

function Room:draw()
    self.map:draw()
	for k,character in pairs(self.characters) do
		character:draw()
	end
end

function Room:update(t)
	self.map:update()
	for k,character in pairs(self.characters) do
		character:update(t)
	end
end

function Room:keypressed(key)
	for k,character in pairs(self.characters) do
		character:keypressed(key)
	end
end

function Room:keyreleased(key)
	for k,character in pairs(self.characters) do
		character:keyreleased(key)
	end
end

function Room:on enter()
end

function Room:on exit()
end

-- MENU
---------------------------------------
Menu = class("Menu")

function Menu:initialize(item list)
	self.menu = lynx.menu(item list, {
		viewport = { 0,  0, 100, 50 },  -- Mi ne vere scias kion ĉi tio signifas.
		offset   = { 0, 50 },           -- Nek ĉi tio, honeste.
		default_height = 20,            -- Ĉi tio diveneblas.
		funcs    = lynx_funcs           -- Vidu ĉe komenco de la dosiero.
	})
end

function Menu:open()
	local functions = {
		["update"]      = { ["func"] = function (t) self.menu:update(t) end,
		                    ["combine"] = false },
		["draw"]        = { ["func"] = function () self.menu:draw() end,
		                    ["combine"] = true },
		["keypressed"]  = { ["func"] = function (key) self.menu:input_key(key, "pressed") end,
		                    ["combine"] = false },
		["keyreleased"] = { ["func"] = function (key) self.menu:input_key(key, "up") end,
		                    ["combine"] = false }
	}
	add state({ ["functions"] = functions, ["name"] = "menuo" })
end

function Menu:close()
	remove state("menuo")
end

-- Prova menuo
-------------------
test menu = Menu:new({
	lynx.text "PROVA MENUO",
	lynx.text "En prova mondo.",
	lynx.text "Farita el plastaĵo,",
	lynx.text "estas bonege…?",
	lynx.button(
		"Presi mojosan tekston",
		function () print "ANAS~ROMPO🤸🦆🤠🦆🤸DANCO~ANAS" end,
		{}
	),
	lynx.button(
		"Presi malmojosan tekston",
		function () print "Mia nacio multe pli bonas ol la via."; end,
		{}
	),
	lynx.button(
		"Fini la mondon",
		love.event.quit,
		{}
	),
	lynx.button(
		"Fermi la menuon",
		function () remove state("menu"); end,
		{}
	)
})

--


-- ROOM
----------------------------------------
Thing = class("Thing")

function Thing:initialize()
    self.things = { }
    self.name = nil
end

function Thing:interact()
end


-- UTIL
--------------------------------------------------------------------------------
-- Install the important 'hook' functions (draw, update, keypressed/released)
-- If any of the 'old' functions passed are not nil, then both the new and
-- old will be added into the new corresponding hook function
-- This function is too god damn long and it makes me want to cry
-- Could be pretty easily shortened, now that I think about it
-- ne mallongigu, suferu

--[[ Miaj pensoj // Tirifto, 2021 // Lua-pens-eldonejo Snopyta, Berlino

Jes ja, kiel estas nune,
oni povus havi ĉiean funkciaron,
al kiu, laŭ sia bezono,
oni funkciojn novajn donus.

Tamen, por priparoli pli klare,
tion al kio celis originale mia mens’,
mi volus diri kelkajn pliajn vortojn,
… .

Esence oni priskribus atendatan konduton de la ludo
per tiel-nomeblaj »statoj«, kiuj povus esti propraj
objektoj.

Ekzemple, la ordinara ludo de stato prezentus tian
konduton, ke ludato moviĝus je premo kaj klavo, kaj
la mondo ordinare ĝisdatiĝus.

Male, povus ekzisti ankaŭ la menua stato aŭ aliaj statoj,
kiuj kondutigus la tutan ludon alimaniere. Ni ankoraŭ devus
registri tiujn statojn per iu ĉiea tabelo.

Kiam ek-funkcius nova stato, ĝi simple aldoniĝus al la tabelo –
ĝi devus aŭ anstataŭigi la antaŭajn tieajn statojn, aŭ indiki,
ke dum ĝia ĉeesto, la konduto de la aliaj statoj tieaj devas
esti malatentata.

La rezulto devus esti la sama kiel ĉe la nuna uzata/proponata
sistemo, kie oni rekte ŝanĝas funkciojn. La diferenco de ĉi tiu
ideo/propono estas, ke oni ne traktas rekte funkciojn, sed rilataj
funkcioj estas enmetitaj en statoj, kaj oni traktas rekte nur statojn
(kaj nerekte do ankaŭ ties funkciojn).

La motivo de tia sistemo estas, ke okupiĝi nur pri po unu stato ĉiufoje
povus esti pli facile/klare, ol okupiĝi pri pluraj funkcioj. Sed ankaŭ
eblas, ke la aldona tavolo de objekto-volvado pli malsimpligus la logikon
de la kodo.


Reĝimoj:
• Ordinara ludo
• Ĉefmenuo
• Ludmenuo
• Batalo
• Parolado
• Interago

perl -ine 's/stat/reĝim' menso de Ĵado.cpp

--]]

-- STATE MANAGEMENT -- State or mode tho
--------------------------------------------------------------------------------
-- Vi ne komprenos la teĥnikan signifon
-- Sed vi komprenos niajn sentojn

-- Por kompreni nian traktadon de reĝimoj pli bone, konsciu ĉi tion:
-- Reĝimoj estas iomete kiel ranoj.

-- Funkciostrukturo:
--	functions = { ["update"] = {["func"] = function, ["combine"] = combine?}, ["draw"] = {function, combine?},
--		["keypressed"] = …, ["keyreleased"] = … }}
-- Tabelstrukturo:
-- 	state = { ["functions"] = functions, ["name"] = string }

function state function(func name, parameter)
	for index,state in pairs(states) do
		if (state.functions[func name].func ~= nil) then
			state.functions[func name].func(parameter)
		end
		if (state.functions[func name].func ~= nil) and (state.functions[func name].combine == false) then
			return
		end
	end
end

function add state(new state)
	insert element(states, new state, 1)
end

function remove state(target name) -- Ⓐ
	for index,state in pairs(states) do
		if (state.name == target name) then -- Hurahhh /\(°v°)/\ 
			remove element(states, index)
			return
		end
	end
end

function remove element(table, index)
	local oldtable = table
	for k,v in pairs(oldtable) do
		if (k > index) then
			table[k - 1] = v
		end
	end
end

function insert element(table, element, index)
	local oldtable = table
	for k,v in pairs(oldtable) do
		if (k >= index) then
			table[k+1] = v
		end
	end
	table[index] = element
end

function is valid direction(number)
	if (-4 <= number) and (number <= -1) then
		return true
	end
	return false
end

-- Replace the keys with the values, and vice versa
function invert table(table)
	local newtable = {}
	for k,v in pairs(table) do
		newtable[v] = k
	end
	return newtable
end
